﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Security.Cryptography;
using System.Threading;

namespace Findr
{
    public delegate void ContactUpdateHandler(FindrServer.Contact[] contacts);
    /// <summary>
    /// Class that communicates with the server
    /// </summary>
    public class Communicator
    {
        public event ContactUpdateHandler ContactUpdate;

        public string ApplicationPath { get; set; }

        public void WriteDebugLog(string s)
        {
            //TODO
        }

        /// <summary>
        /// Class that stores the state of the asynchronous request
        /// </summary>
        public class RequestState
        {
            /// <summary>
            /// Holds the request
            /// </summary>
            public WebRequest Request;
            /// <summary>
            /// Store any data in this
            /// </summary>
            public object Data;
            /// <summary>
            /// Holds the UrlString to match up results (Database lookup, etc).
            /// </summary>
            public string SiteUrl;
            /// <summary>
            /// Constructor of the RequestState class
            /// </summary>
            /// <param name="request">Request</param>
            /// <param name="data">Object Data</param>
            /// <param name="siteUrl">URL string</param>
            public RequestState(WebRequest request, object data, string siteUrl)
            {
                this.Request = request;
                this.Data = data;
                this.SiteUrl = siteUrl;
            }
        }

        /// <summary>
        /// Constructor of the Communicator class (creates blank)
        /// </summary>
        public Communicator()
        {
        }

        /// <summary>
        /// Hashes an input string and returns the hash as a 32 character hexadecimal string.
        /// </summary>
        /// <param name="input">Input string</param>
        /// <returns>Hashed string</returns>
        private string getMd5Hash(string input)
        {
            // Create a new instance of the MD5CryptoServiceProvider object.
            MD5 md5Hasher = MD5.Create();

            // Convert the input string to a byte array and compute the hash.
            byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));

            // Create a new Stringbuilder to collect the bytes
            // and create a string.
            StringBuilder sBuilder = new StringBuilder();

            // Loop through each byte of the hashed data 
            // and format each one as a hexadecimal string.
            for (int i = 0; i < data.Length; i++)
            {
                sBuilder.Append(data[i].ToString("x2"));
            }

            // Return the hexadecimal string.
            return sBuilder.ToString();
        }

        private void OnContactUpdate(FindrServer.Contact[] contacts)
        {
            if (ContactUpdate != null)
            {
                ContactUpdate(contacts);
            }
        }

        /// <summary>
        /// Begins to request the contact list from the server
        /// </summary>
        /// <param name="Settings">Settings object with user credentials</param>
        public void GetContactList()
        {
            try
            {
                FindrServer.FindrService findrService = new global::Findr.FindrServer.FindrService();
                findrService.Timeout = 10000;
                findrService.Url = Settings.ServerUrl + "services.php";
                findrService.BeginGetContactList(Settings.UserName, getMd5Hash(Settings.UserPass), new AsyncCallback(GetContactListCallback), findrService);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
        /// <summary>
        /// Handles the contact list update response
        /// </summary>
        /// <param name="result">Asynchronous event result</param>
        public void GetContactListCallback(IAsyncResult result)
        {
            try
            {
                FindrServer.FindrService findrService = (FindrServer.FindrService)result.AsyncState;
                FindrServer.Contact[] contacts = findrService.EndGetContactList(result);

                OnContactUpdate(contacts);
            }
            catch (System.Web.Services.Protocols.SoapException e)
            {
                switch (e.Message)
                {
                    case "LOGIN_ERROR": WriteDebugLog("Invalid username/password!"); break;
                    case "EMPTY_ERROR": WriteDebugLog("No contacts."); break;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
        /// <summary>
        /// Begins to report the position to the server
        /// </summary>
        /// <param name="Settings">Settings object with user credentials</param>
        /// <param name="gpsData">Gps object with position data</param>
        public void UpdatePosition(Interfaces.IAbsoluteData position)
        {
            try
            {
                FindrServer.FindrService findrService = new global::Findr.FindrServer.FindrService();
                findrService.Timeout = 10000;
                findrService.Url = Settings.ServerUrl + "services.php";
                findrService.BeginUpdatePosition(Settings.UserName, getMd5Hash(Settings.UserPass), position.Latitude, position.Longitude, Settings.IsLogged, new AsyncCallback(UpdatePositionCallback), findrService);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
        /// <summary>
        /// Handles the position report response
        /// </summary>
        /// <param name="result">Asynchronous event result</param>
        public void UpdatePositionCallback(IAsyncResult result)
        {
            try
            {
                FindrServer.FindrService findrService = (FindrServer.FindrService)result.AsyncState;
                string queryResult = findrService.EndUpdatePosition(result);
                // TODO: itt van egy exception ("Client found response content type of 'text/html', but expected 'text/xml'"), aminek nagyon nem kéne lennie! Lehetséges, hogy .NET CF bug, nem saját

                switch (queryResult)
                {
                    case "UPDATED": WriteDebugLog("Position updated."); break;
                    case "CREATED": WriteDebugLog("Position set."); break;
                    default: WriteDebugLog("Illegal operation."); break;
                }
            }
            catch (System.Web.Services.Protocols.SoapException e)
            {
                switch (e.Message)
                {
                    case "LOGIN_ERROR": WriteDebugLog("Invalid username/password!"); break;
                    case "QUERY_ERROR":
                    default: WriteDebugLog("Illegal operation."); break;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
        /// <summary>
        /// Begins to send a message
        /// </summary>
        /// <param name="Settings">Settings object with user credentials</param>
        /// <param name="nameUserTo">Username of the recipient</param>
        /// <param name="message">Message text</param>
        public void SendMessage(string nameUserTo, string message)
        {
            try
            {
                FindrServer.FindrService findrService = new global::Findr.FindrServer.FindrService();
                findrService.Timeout = 10000;
                findrService.Url = Settings.ServerUrl + "services.php";
                findrService.BeginSendMessage(Settings.UserName, getMd5Hash(Settings.UserPass), nameUserTo, message, new AsyncCallback(SendMessageCallback), findrService);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                WriteDebugLog("Couldn't connect to server.");
            }
        }
        /// <summary>
        /// Handles the message sending response
        /// </summary>
        /// <param name="result">Asynchronous event result</param>
        public void SendMessageCallback(IAsyncResult result)
        {
            try
            {
                FindrServer.FindrService findrService = (FindrServer.FindrService)result.AsyncState;
                string queryResult = findrService.EndSendMessage(result);
                if (queryResult == "SENT") WriteDebugLog("Message sent.");
            }
            catch (System.Web.Services.Protocols.SoapException e)
            {
                switch (e.Message)
                {
                    case "LOGIN_ERROR": WriteDebugLog("Invalid username/password!"); break;
                    case "ILLEGAL_OPERATION": WriteDebugLog("Illegal operation."); break;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                WriteDebugLog("Couldn't connect to server.");
            }
        }
        /// <summary>
        /// Begins to request message list from the server
        /// </summary>
        /// <param name="Settings">Settings object with user credentials</param>
        public void GetMessages()
        {
            try
            {
                FindrServer.FindrService findrService = new global::Findr.FindrServer.FindrService();
                findrService.Timeout = 10000;
                findrService.Url = Settings.ServerUrl + "services.php";
                findrService.BeginGetMessages(Settings.UserName, getMd5Hash(Settings.UserPass), new AsyncCallback(GetMessagesCallback), findrService);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                WriteDebugLog("Couldn't connect to server.");
            }
        }
        /// <summary>
        /// Handles the message request response
        /// </summary>
        /// <param name="result">Asynchronous event result</param>
        public void GetMessagesCallback(IAsyncResult result)
        {
            try
            {
                FindrServer.FindrService findrService = (FindrServer.FindrService)result.AsyncState;
                FindrServer.Message[] messages = findrService.EndGetMessages(result);
               // updateMessagesCallback(messages);
                WriteDebugLog("Messages refreshed.");
                // TODO: itt hívni DeleteReadMessages()-t
            }
            catch (System.Web.Services.Protocols.SoapException e)
            {
                switch (e.Message)
                {
                    case "LOGIN_ERROR": WriteDebugLog("Invalid username/password!"); break;
                    case "EMPTY_ERROR": WriteDebugLog("No contacts."); break;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                WriteDebugLog("Couldn't connect to server.");
            }
        }
        /// <summary>
        /// Begins to request adding a new follower
        /// </summary>
        /// <param name="Settings">Settings object with user credentials</param>
        /// <param name="friend">Username of the new follower</param>
        public void AddFollower(string friend)
        {
            try
            {
                FindrServer.FindrService findrService = new global::Findr.FindrServer.FindrService();
                findrService.Timeout = 10000;
                findrService.Url = Settings.ServerUrl + "services.php";
                findrService.BeginAddFollower(Settings.UserName, getMd5Hash(Settings.UserPass), friend, new AsyncCallback(AddFollowerCallback), findrService);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                WriteDebugLog("Couldn't connect to server.");
            }
        }
        /// <summary>
        /// Handles the new follower request response
        /// </summary>
        /// <param name="result">Asynchronous event result</param>
        public void AddFollowerCallback(IAsyncResult result)
        {
            try
            {
                FindrServer.FindrService findrService = (FindrServer.FindrService)result.AsyncState;
                string res = findrService.EndAddFollower(result);
                WriteDebugLog("Messages refreshed.");
            }
            catch (System.Web.Services.Protocols.SoapException e)
            {
                switch (e.Message)
                {
                    case "LOGIN_ERROR": WriteDebugLog("Invalid username/password!"); break;
                    case "QUERY_ERROR": WriteDebugLog("Query error."); break;
                    case "NO_SUCH_USER": WriteDebugLog("No such user."); break;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                WriteDebugLog("Couldn't connect to server.");
            }
        }

        public void GetContactPicture(string friend)
        {
            try
            {
                FindrServer.FindrService findrService = new global::Findr.FindrServer.FindrService();
                findrService.Timeout = 10000;
                findrService.Url = Settings.ServerUrl + "services.php";
                findrService.BeginGetContactPicture(friend, new AsyncCallback(GetContactPictureCallback), findrService);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                WriteDebugLog("Failed to get contact picture for " + friend + ".");
            }
        }

        public void GetContactPictureCallback(IAsyncResult result)
        {
            try
            {
                FindrServer.FindrService findrService = (FindrServer.FindrService)result.AsyncState;
                FindrServer.ContactPicture res = findrService.EndGetContactPicture(result);

                // Convert Base64 String to byte[]
                byte[] imageBytes = Convert.FromBase64String(res.picture);
                MemoryStream ms = new MemoryStream(imageBytes, 0, imageBytes.Length);

                // Convert byte[] to Image
                ms.Write(imageBytes, 0, imageBytes.Length);
                System.Drawing.Bitmap image = new System.Drawing.Bitmap(ms);

                image.Save(ApplicationPath + "\\plugins\\Findr\\avatars\\" + res.user + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
                
                WriteDebugLog("Contact picture updated for " + res.user + ".");
            }
            catch (System.Web.Services.Protocols.SoapException e)
            {
                switch (e.Message)
                {
                    case "QUERY_ERROR": WriteDebugLog("Query error."); break;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                WriteDebugLog("Failed to get contact.");
            }
        }
    }
}
